package com.cicadasmall.system.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.extra.servlet.ServletUtil;
import com.cicadasmall.common.base.BaseService;
import com.cicadasmall.common.enums.ActiveStateEnum;
import com.cicadasmall.common.enums.UserTypeEnum;
import com.cicadasmall.common.exception.ServiceException;
import com.cicadasmall.common.func.Fn;
import com.cicadasmall.data.domain.UserDO;
import com.cicadasmall.data.domain.UserConnectionDO;
import com.cicadasmall.data.mapper.SysUserConnectionMapper;
import com.cicadasmall.system.service.IUserConnectionService;
import com.cicadasmall.system.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * <p>
 * 用户社交账号绑定 服务实现类
 * </p>
 *
 * @author jin
 * @since 2020-03-27
 */
@Slf4j
@Service
public class UserConnectionServiceImpl extends BaseService<SysUserConnectionMapper, UserConnectionDO> implements IUserConnectionService {

    @Resource
    private WxMaService wxMaService;

    @Resource
    private WxMpService wxMpService;

    @Resource
    private IUserService userService;

    @Override
    public UserConnectionDO findByOpenId(String openId, String providerId) {
        return baseMapper.selectOne(getLambdaQueryWrapper()
                .eq(UserConnectionDO::getProviderId, providerId)
                .eq(UserConnectionDO::getProviderUserId, openId)

        );
    }

    @Override
    public UserConnectionDO findByOpenIdAndUnionId(String openId, String unionId, String providerId) {
        return baseMapper.selectOne(getLambdaQueryWrapper()
                .eq(UserConnectionDO::getProviderUserId, openId)
                .eq(UserConnectionDO::getProviderUnionId, unionId)
                .eq(UserConnectionDO::getProviderId, providerId)
        );
    }

    private UserConnectionDO createUserConnection(String provider, String openId, String unionId, String nickname, String avatar) {

        log.info("创建用户绑定：{}", provider);

        UserDO user = new UserDO();
        user.setUsername(provider.concat("_").concat(user.getUid().toString()));
        user.setStatus(ActiveStateEnum.启用.getCode());
        user.setUserType(UserTypeEnum.用户.getCode());
        user.setStatus(ActiveStateEnum.启用.getCode());
        user.setRealName(nickname);
        user.setAvatar(avatar);
        user.setCreateIp(ServletUtil.getClientIP(((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest()));
        userService.save(user);

        log.info("新用户创建完成！{}", user);

        UserConnectionDO userConnection = new UserConnectionDO();
        userConnection.setProviderId(provider);
        userConnection.setRankNum(1);
        userConnection.setUserId(user.getUsername());
        userConnection.setProviderUserId(openId);
        userConnection.setProviderUnionId(unionId);
        save(userConnection);

        log.info("用户绑定完成！{}", userConnection);
        return userConnection;
    }

    @Transactional
    @Override
    public UserConnectionDO findOrSaveByJsCode(String jsCode, boolean createUser) throws UsernameNotFoundException, WxErrorException {

        UserConnectionDO userConnection;
        WxMaJscode2SessionResult wxMaJscode2SessionResult = wxMaService.getUserService().getSessionInfo(jsCode);

        //Jin 根据openid 或者 unionId 查询用户小程序绑定信息
        if (Fn.isNotEmpty(wxMaJscode2SessionResult.getUnionid())) {
            userConnection = findByOpenIdAndUnionId(wxMaJscode2SessionResult.getOpenid(), wxMaJscode2SessionResult.getUnionid(), "wxma");
        } else {
            userConnection = findByOpenId(wxMaJscode2SessionResult.getOpenid(), "wxma");
        }

        //判断是否需要绑定用户信息
        if (Fn.isNull(userConnection)) {
            if (createUser) {
                userConnection = createUserConnection("wxma", wxMaJscode2SessionResult.getOpenid(), wxMaJscode2SessionResult.getUnionid(), "", "");
                userConnection.setProviderSessionKey(wxMaJscode2SessionResult.getSessionKey());
                updateById(userConnection);
            } else {
                throw new ServiceException("用户未绑定小程序信息！");
            }
        }
        return userConnection;
    }

    @Transactional
    @Override
    public UserConnectionDO findOrSaveByCode(String code, boolean createUser) throws WxErrorException {
        UserConnectionDO userConnection;
        WxMpOAuth2AccessToken wxMpOAuth2AccessToken = wxMpService.oauth2getAccessToken(code);

        //Jin 根据openid 或者 unionId 查询用户小程序绑定信息
        if (Fn.isNotEmpty(wxMpOAuth2AccessToken.getUnionId())) {
            userConnection = findByOpenIdAndUnionId(wxMpOAuth2AccessToken.getOpenId(), wxMpOAuth2AccessToken.getUnionId(), "wxmp");
        } else {
            userConnection = findByOpenId(wxMpOAuth2AccessToken.getOpenId(), "wxmp");
        }

        //判断是否需要绑定用户信息
        if (Fn.isNull(userConnection)) {
            if (createUser) {
                WxMpUser wxMpUser = wxMpService.oauth2getUserInfo(wxMpOAuth2AccessToken, "zh_CN");
                userConnection = createUserConnection("wxmp", wxMpOAuth2AccessToken.getOpenId(), wxMpOAuth2AccessToken.getUnionId(), wxMpUser.getNickname(), wxMpUser.getHeadImgUrl());
                userConnection.setDisplayName(wxMpUser.getNickname());
                userConnection.setImageUrl(wxMpUser.getHeadImgUrl());
                userConnection.setExpireTime((long) wxMpOAuth2AccessToken.getExpiresIn());
                userConnection.setAccessToken(wxMpOAuth2AccessToken.getAccessToken());
                userConnection.setRefreshToken(wxMpOAuth2AccessToken.getRefreshToken());
                updateById(userConnection);
            } else {
                throw new ServiceException("用户未绑定公众号信息！");
            }
        }

        return userConnection;
    }

    @Override
    public UserConnectionDO findUserIdAndProviderId(String userId, String providerId) {
        UserDO user = userService.getById(userId);
        if (Fn.isNull(user)) {
            return null;
        }
        return baseMapper.selectOne(getLambdaQueryWrapper()
                .eq(UserConnectionDO::getUserId, user.getUsername())
                .eq(UserConnectionDO::getProviderId, providerId)
        );
    }
}
